home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 6 / Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso / 045a / btp15.zip / BTP.DOC next >
Text File  |  1991-11-09  |  23KB  |  495 lines

  1. BTP.DOC
  2. (C) 1991 John C. Leon
  3. Documentation for BTP V1.5, 11/9/91
  4. BTP - The Btrieve Unit for Turbo Pascal 6.0
  5. (* ------------------------------------------------------------------------ *)
  6.  
  7.  
  8. INTRODUCTION TO BTP
  9. -------------------
  10.  
  11. Btrieve is a record manager sold by Novell.  Btrieve has been around a long
  12. time.  It takes many forms today...with versions for Dos, Windows 3.0,
  13. OS/2, and a "client/server" form when used with Novell Netware.  This unit has
  14. been tested only with Btrieve for Dos, Version 5.10a, with all patches
  15. available thru 10/27/91 applied.
  16.  
  17. Btrieve files have no inherent structure to differentiate fields.  Your own
  18. code provides that functionality.  The BTP product is the only programmer's
  19. toolkit of its kind (to my knowledge) available to TP/Btrieve programmers
  20. offering an extra-ordinarily simple means of Btrieve file management...a
  21. well-thought out design of data structures and a complete guide (through the
  22. example programs) to deal with Btrieve files as objects.  Further, BTP
  23. provides the means to make any Btrieve call SIMPLY and with CONFIDENCE, with
  24. encapsulation of your file's position block.  The most intriguing capability
  25. for many will be the ability to use the relatively new get/step extended calls
  26. WITH EASE; it is these more complex, yet important functions, that daunt even
  27. the most ardent fans of Btrieve.  NO MORE!
  28.  
  29. The "universal" Btrieve call, which is made by calling a TP function supplied
  30. by Novell with the Btrieve product, takes six parameters.  Keeping track of
  31. multiple Btrieve files and the variables for all six parameters for each
  32. file in a complex application using traditional programming techniques takes
  33. discipline and a masochistic attention to detail; I consider myself
  34. disciplined but I'm NOT masochistic.  Btrieve programming screams out for the
  35. simplification possible with object-oriented programming and the record
  36. structures provided by Turbo Pascal 6.0.
  37.  
  38. The BTP product was born to remove the drudgery from Btrieve programming.  BTP
  39. includes a number of sound data structures/objects, including these four:
  40. TRecMgr, BFile, BFixed, and BFileExt.  TRecMgr and BFile are base objects;
  41. they are direct descendants of TP6's TObject.  BFixed and BFileExt are both
  42. direct descendants of BFile.
  43.  
  44. TRECMGR  is of limited usefulness (see the unit source code and VERSION.PAS),
  45. -------  but it does provide the ability to make non-file oriented calls
  46.          (stop, reset, version, et al).
  47.  
  48. BFILE    is what you'll use for virtually all standard, fixed length Btrieve
  49. -----    files when not using extended calls; it will probably be your
  50.          workhorse.
  51.  
  52. BFIXED   has structures to support ANY standard, fixed length Btrieve file.
  53. ------   BFixed is used in the CRUNCHx.PAS programs provided, which can 'clone
  54.          and squish' (remove dead space) from any standard, fixed length
  55.          Btrieve file.  BFixed is useful for working on standard fixed length
  56.          files of an unknown nature when you need to perform *record* oriented
  57.          functions with no care about field definitions.
  58.  
  59. BFILEEXT is used for any Btrieve file for which you'll be using extended calls
  60. -------- (note there is no specific support for the extended insert call, but
  61.          CRUNCH2.PAS and CRUNCH3.PAS contain examples of how to make such
  62.          calls).
  63.  
  64. Quite simply, your TYPE declarations define your Btrieve files as descendants
  65. of one of the three file-oriented objects provided in BTP and described above.
  66. Those declarations can include definitions of your file's field structures,
  67. and provide the required data and key buffers.  These programmer-defined
  68. additions to the BTP objects are encapsulated in the descendant object, along
  69. with the data fields inherited, which include a description of the file's
  70. structure and the file's position block.  Immediately upon instantiation of a
  71. BTP descendant object, you have access to any file's structure and stats.  You
  72. also have an 'isolated' position block and the required buffers for Btrieve
  73. calls.
  74.  
  75. In the case of a BFile descendant, you override (replace) a single object
  76. method, and can make your Btrieve calls with just two parameters: the Btrieve
  77. op code and key number.  Use of the 4 get/step extended calls is only
  78. marginally more difficult.  In order to use extended calls, your object must
  79. be a descendant of BFileExt, and must override two methods: one for standard
  80. calls, and one for the extended calls.  BFileExt includes pointers to two
  81. collections, which are automatically initialized when the object's
  82. constructor is called.  The first collection is for the filter logic terms,
  83. the second is for the field extractor specs.  Your program simply inserts
  84. items into these collections.  The rest, including determining buffer lengths
  85. and structuring the outgoing buffer, is handled internally by the object's
  86. methods.
  87.  
  88. Note that this unit's initialization section does a HALT if the Btrieve
  89. record manager isn't resident...you never have to code the test yourself.
  90. Just use this unit!
  91.  
  92.  
  93. USING THE BFILE OBJECT
  94. ----------------------
  95.  
  96. This unit gives you a technique, a shorthand way to get into Btrieve files
  97. and manipulate them.  For example, to open a file and encapsulate its stats
  98. into your Btrieve file object, you need only do:
  99.  
  100.      ObjectName^.Init(BDosFileName, Normal)
  101.      (BDosFileName is a valid Btrieve file name, and 'Normal' is the open
  102.       mode...see BTP.PAS for the various constants defined.)
  103.  
  104. Similary, to close a file, you simply do:
  105.  
  106.      BStatus := ObJectName^.Close;
  107.  
  108. The BFile object defined herein is the heart of this unit.  BFile is NOT an
  109. abstract object!!  It can be used as is, for example, if all you want to do
  110. is to get stats (see STATS.PAS).  However, if you wish to do any normal
  111. record-oriented functions, you will want to extend the object by adding one
  112. data element, a free-union variant record, to the BFile definition.  This
  113. record will contain your field definitions.  Lastly, you are *required* to
  114. override the BT function.  Here's an example.
  115. ..............................................................................
  116. TYPE
  117.  
  118.   MyFields  = record
  119.               case integer of
  120.               1: (Field1 : array[1..4] of char;  (obviously use size and type)
  121.                   Field2 : array[1..10] of char; (of your fields here!       )
  122.                   (rest of fields go here also)
  123.                   KeyBuf : array[1..4] of char); (size to largest key length )
  124.               2: (DBuffer: array[1..14] of char);(size to record length      )
  125.               3: (Position: array[1..2] of word);(high word returned first!  )
  126.                   end;                           (useful after a GET POSITION)
  127.  
  128.   PMyObject = ^MyObject;
  129.   MyObject  = object(BFile)        (MyObject is now a descendant of BFile.   )
  130.               Fields : MyFields;
  131.               function BT(OpCode, Key: integer); integer; virtual;
  132.               end;
  133.  
  134. VAR
  135.  
  136.   MyFile    : PMyObject;
  137.  
  138.  
  139. (This is the required override of the BT function for all standard, fixed
  140.  length files.  As the base object has no field structures, it cannot include
  141.  this function...you must override it by REPLACING it as shown here.)
  142.  
  143. function MyObject.BT(OpCode, Key:integer);integer;
  144. begin                             (DBufferLen is reset here as it may need to)
  145.    DBufferLen := Specs.RecLen;    (be changed on return from some ops.       )
  146.    BT := Btrv(OpCode, PosBlk, Fields, DBufferLen, Fields.KeyBuf, Key);
  147. end;
  148. ..............................................................................
  149.  
  150. The record variable itself should be used as the Btrieve data buffer
  151. parameter.  The data buffer length is reset to the file's record length before
  152. making the call.  The KeyBuf field of the MyFields record should be used in
  153. all Btrieve calls as the Btrieve key buffer.
  154.  
  155. As you can see, your BFile descendant will incorporate the universal Btrieve
  156. function call in a compact, elegant format.  Thanks to OOP and encapsulation,
  157. you are guaranteed that each descendant's own key and data buffers are used
  158. since each object has its private copy of the .BT method.
  159.  
  160. As an aside, the .BT function cannot be fully incorporated into the base BFile
  161. object since the base object has no data fields corresponding to your file's
  162. fields, and thus cannot contain a properly sized key buffer or data buffer.
  163. The override is necessary because neither the BFile object nor Btrieve itself
  164. know anything about fields or the appropriate size for your buffers.
  165.  
  166. In fact, to assure you don't call the .BT function of BFile directly, BFile.BT
  167. includes a call to TP6's Abstract method, which will crash your program with a
  168. runtime error unless you replace it, presumably with the function as defined
  169. and recommended here.
  170.  
  171. For your reference, the base BFile object is all of 975 bytes in size.  The
  172. BFile descendant used in EXAMPLE1.PAS, even with its additional methods and
  173. data fields, is only 1025 bytes in size.  In contrast, the BFixed object is
  174. 5320 bytes in size, due to its inclusion of maximum size buffers for a file
  175. with records of up to the maximum size of 4090 and maximum key length of 255.
  176.  
  177. Assume a Btrieve file named 'DosFile'.  To initialize the BFile descendant
  178. defined above:
  179.  
  180.      MyFile := new(PMyObject, Init('DosFile', Normal));
  181.  
  182. The BFile constructor/initialization method will do a Btrieve open operation,
  183. using the open mode you provide as the second parameter to the Init call.
  184. After the open operation, a stat operation is performed.  The Btrieve
  185. filespec and key specs, retrieved by the stat call, are incorporated into
  186. BFile's data fields.  Those stats can then be read out at will simply by
  187. referencing the object's data fields in the following manner:
  188.  
  189.      MyFile^.Specs.PageSize, or MyFile^.NumRecs, etc.
  190.  
  191. The stat fields defined in the object include the number of keys, the total
  192. number of key segments, the page size, the number of records in the file
  193. when the INIT was performed, the file flags...in short, the complete Btrieve
  194. filespec.  All other stats can be derived from the object's data fields if
  195. needed.  If you ever need to refresh the stat data to refresh the record
  196. count, for example, you could simply:
  197.  
  198.      BStatus := MyFile^.Close;
  199.      dispose(MyFile, Done);
  200.      MyFile := new(PMyObject, Init('DosFile', Normal));
  201.      HowMany := MyFile^.NumRecs;
  202.  
  203. Of course, you could always do a Stat call directly and deal with the
  204. results, but the BTP way is so EASY!!  MyFile^.NumRecs, a longint, is
  205. available immediately after the Init call.
  206.  
  207. Btrieve operations with BTP are performed by calling the BT function with JUST
  208. TWO PARAMETERS.  Here's a simple step next call:
  209.  
  210.   BStatus := MyFile^.BT(BStepNext, 2);
  211.  
  212.   (BStatus is a public integer var from this unit.  BStepNext is a public
  213.    constant.  A number of public constants are defined in this unit that can
  214.    help make your code more readable.)
  215.  
  216. There is some nominal overhead in the BFile object.  It allocates space for
  217. the maximum of 24 keys/segments, and for an alternate collating sequence,
  218. whether one is used in the file or not.  This simply means a maximum of 633
  219. bytes per open file could be wasted.  This maximum would apply to a standard
  220. Btrieve file that had just one unsegmented key.  The figure of 368 is derived
  221. as follows:
  222.  
  223.                  665 maximum bytes if the Btrieve max of 24 keys/
  224.                      segments and an alternate collating sequence is used
  225.                -  16 bytes to hold the Btrieve file specs (stats)
  226.                -  16 bytes for the minimum required 1 Btrieve key spec
  227.                  ---
  228.                  633 bytes maximum possible waste
  229.  
  230. In addition, my convention of supplying the BFile descendant with a built-in
  231. key-buffer, arbitrarily sized to the length of the largest size key, could be
  232. overhead. I don't mind that a bit, and I don't think you should.  In my opinion,
  233. the code space saved by using this unit's structures and techniques versus
  234. the code resulted from using any other method is significant and more than
  235. offsets some truly nominal memory costs.
  236.  
  237. The benefits of referencing any open Btrieve file and its components or stats
  238. by name makes using the Btrieve record manager a snap.  Add to your descendant
  239. appropriate methods to get/set your fields and the key and data buffers and
  240. you'll be much more productive.
  241.  
  242. Your app's code is responsible for stuffing key values into the key buffer,
  243. getting or setting the data buffer, et al, as usual.  However, you gain the
  244. ease of accessing all Btrieve ops in a distinct shorthand, accessing those
  245. buffers and fields by name, and can rest assured that all parameters are
  246. passed and filled.  See EXAMPLE1.PAS for a full working model of this
  247. technique.
  248.  
  249. You will note that the examples sometimes break one of OOP's rules by
  250. accessing data fields directly.  In my own apps I will typically have at
  251. least a couple of additional methods in my BFile descendants for getting and
  252. setting the key buffers and data buffers...such as the following three:
  253.  
  254. (pass whatever string you want to stuff in the key buffer)
  255. procedure MyObject.SetKeyBuf(KeyBufString:string);
  256. var
  257.    Counter,
  258.    LengthKeyBufString : integer;
  259. begin
  260. LengthKeyBufString := length(KeyBufString);
  261. if LengthKeyBufString > 12 then     (replace '12' with length of your KeyBuf)
  262.    LengthKeyBufString := 12;
  263. move(KeyBufString[1], Fields.KeyBuf, LengthKeyBufString);
  264. if LengthKeyBufString < 12 then
  265.    for Counter := (LengthKeyBufString+1) to 12 do
  266.       Fields.KeyBuf[Counter] := ' '; (pad with trailing blanks)
  267. end;
  268.  
  269. function MyObject.GetKeyBuf:string;
  270. begin
  271.    GetKeyBuf := Fields.KeyBuf;
  272. end;
  273.  
  274. function MyObject.GetDBuffer:string;
  275. begin
  276.    GetDBuffer := Fields.DBuffer;
  277. end;
  278.  
  279.  
  280. USING THE BFILEEXT OBJECT
  281. -------------------------
  282.  
  283. There were several assumptions coded into the BTP unit to make a working
  284. structure for the get/step extended calls (see the declaration of data types
  285. for BFileExt in the unit's source code):
  286.  
  287.   1. That the required data buffer will never be more than 32767 bytes.
  288.   2. That values used for a filter's logic terms will never be longer
  289.      than 255 bytes.
  290.  
  291. Beyond these assumptions, which you can change if necessary by changing the
  292. unit's source or by defining your own descendants, BTP enables you to make any
  293. of the four get/step extended calls with ease.  The best examples of how to do
  294. so are in the example programs CRUNCH3.PAS and EXAMPLE2.PAS.
  295.  
  296. Admittedly, constructing outgoing buffers for Btrieve's extended calls can
  297. be frustrating.  Btrieve is hostile in this regard.  BTP reduces this
  298. drudgery to a couple of additional setup statements!
  299.  
  300. Recall that the outgoing buffer for these extended calls requires several
  301. data structures occupying contiguous bytes in the buffer:  a header, a
  302. filter, optional filter logic terms, an extractor, and at least one field
  303. extractor spec.  After initializing the data buffer, you must calculate
  304. the buffer length for the Btrieve call to be the larger of the outgoing or
  305. incoming buffers!  SHEESH!  The BTP way of dealing with this is as follows:
  306.  
  307.    1:  HEADER is handled internally.  Don't mess with it.  Yes, even setting
  308.        the buffer length in the header is handled internally.
  309.    2:  FILTER's fields MUST be assigned by your program.
  310.    3:  FILTERSPEC is a TP collection that may or may not hold any objects,
  311.        depending on your program's needs.
  312.    4:  EXTRACTOR's fields MUST be assigned by your program.
  313.    5:  EXTRACTORSPEC is a TP collection that must hold at least one object.
  314.    6:  EXTDBUFFER, the data buffer for these 4 extended calls, is handled
  315.        internally.  Don't mess with it, except to use it as shown below and in
  316.        EXAMPLE2.PAS when you override BFileExt.BTExt.
  317.    7.  Buffer structuring is totally transparent to your program.  Simply
  318.        override the BFileExt.BTExt function as shown, and you're there!
  319.        Believe me, this took some work!  The standard TP6 'ForEach' iterator
  320.        is used to take every item in the two collections and account for
  321.        them in structuring the outgoing data buffer.
  322.  
  323. The BFileExt.BTExt method must be overridden, with the override calling the
  324. ancestor; i.e. your override must be of a standard form, and must *first* call
  325. BFileExt.BTExt.  This is because BFileExt.BTExt is what sets the buffer length
  326. and constructs the buffer.  This is in contrast to BFile.BT or BFileExt.BT,
  327. which must be overridden by REPLACING them.
  328.  
  329. Since Btrieve's get/step extended calls permit use of filters based on field
  330. values or on a user (program) supplied value, there are two constructors for
  331. the logic term object: INITF (initialize to compare with field) and,
  332.                        INITV (initialize to compare with value).
  333. Refer to the BTP source code for a list of the parameters required by these
  334. two constructors.
  335.  
  336. I strongly urge you to review EXAMPLE2.PAS for a full working model of using
  337. extended calls with BTP.  The following is a skeletal example.
  338.  
  339. .............................................................................
  340. USES BTP;
  341.  
  342. TYPE
  343.    MyFields  = record
  344.                   case integer of
  345.                   1: (First   :array[1..10] of char;
  346.                       Last    :array[1..20] of char;
  347.                       KeyBuf  :array[1..20] of char); (sized to largest key)
  348.                   2: (DBuffer :array[1..30] of char); (sized to rec length)
  349.                   3: (Position:array[1..2] of word); (useful after a GET POS)
  350.                   end;
  351.    PMyObject = ^MyObject;
  352.    MyObject  = object(BFileExt)
  353.                   Fields: MyFields;
  354.                   function BT(OpCode, Key: integer): integer; virtual;
  355.                   function BTExt(OpCode, Key: integer): integer; virtual;
  356.                   end;
  357.  
  358. VAR
  359.    MyFile    = PMyObject;
  360.    Counter   = integer;
  361.    X         = string;
  362.    Value     = TCharArray;  {TCharArray is a data type in BTP...an array of
  363.                              255 chars.}
  364.  
  365. {NOTE that you can use these overrides verbatim for each and every descendant
  366.  if you follow the naming conventions outlined in this documentation and in
  367.  the example programs.}
  368.  
  369. function MyObject.BT(OpCode, Key: integer): integer;
  370. begin
  371.    DBufferLen := Specs.RecLen;
  372.    BT := Btrv(OpCode, PosBlk, Fields, DBufferLen, Fields.KeyBuf, Key);
  373. end;
  374.  
  375. function MyObject.BTExt(OpCode, Key: integer): integer;
  376. begin
  377.    BStatus := BFileExt.BTExt(OpCode, Key);   (MUST call ancestor method!!!)
  378.    BTExt   := Btrv(OpCode, PosBlk, ExtDBuffer^.Entire, DBufferLen,
  379.                    Fields.KeyBuf, Key);
  380. end;
  381.  
  382. BEGIN
  383.  
  384. MyFile := new(PMyObject, Init('Example', ReadOnly));
  385. {WHAM!  File 'Example' is now open in read only mode, with it's structure and
  386.  stats encapsulated in fields of the object.}
  387.  
  388. with MyFile^ do                       {Perform required data initializations.}
  389.    begin                              {This one code section takes care of   }
  390.    Filter.MaxSkip       := 50;        {initializing the filter and extractor.}
  391.    Filter.NumLogicTerms :=  2;
  392.    Extractor.NumRecords :=  5;
  393.    Extractor.NumFields  :=  1;
  394.    end;
  395.  
  396. {Now specify filter logic terms.  We'll setup filter to use 'Leon' (a Last
  397.  Name) as a value for filtering.}
  398. X := 'Leon';
  399. for Counter := 1 to length(X) do
  400.    Value[Counter] := X[Counter];
  401. for Counter := (length(X) + 1) to 255 do
  402.    Value[Counter] := ' ';                   {Pad the array w/trailing blanks.}
  403.  
  404. {Now use one statement to initialize the filter logic term object and insert
  405.  it into the FilterSpec collection.  Note use of the INITV constructor vs
  406.  the INITF constructor.}
  407.  
  408. with MyFile^.FilterSpec^ do
  409.    insert(new(PFilterSpec, InitV(BString, 20, 10, Equal, NextTermAnd, Value)));
  410.  
  411. {Now setup another logic term to specify a first name search condition!}
  412. X := 'John';
  413. for Counter := 1 to length(X) do
  414.    Value[Counter] := X[Counter];
  415. for Counter := (length(X) + 1) to 255 do
  416.    Value[Counter] := ' ';
  417.  
  418. {Here's the statement to initialize the second filter logic term object and
  419.  insert it into the FilterSpec collection provided and initialized for you.}
  420.  
  421. with MyFile^.FilterSpec^ do
  422.    insert(new(PFilterSpec, InitV(BString, 10, 0, Equal, LastTerm, Value)));
  423.  
  424. {Let's set up to extract an entire record, not just particular fields.}
  425. {1st parameter to the Init is the length of the item to extract (here the
  426.  record length).  2nd parameter is offset from which to begin extraction}
  427. with MyFile^.ExtractorSpec^ do
  428.    insert(new(PExtSpec, Init(MyFile^.Specs.RecLen, 0)));
  429.  
  430. {Oh, yea...need to establish a position before using these calls, so let's do
  431.  a Get First before the extended call!}
  432. BStatus := MyFile^.BT(BGetFirst, Zero);
  433.  
  434. {Now let's get on with it and make the extended call...}
  435. BStatus := MyFile^.BTExt(BGetNextExt, Zero);
  436.  
  437. {Take advantage of the data types and Pascal record structures provided to
  438.  see how many records were returned in this call.}
  439. writeln('Number of records returned with name ''John Leon'' is ',
  440.         MyFile^.ExtDBuffer^.NumRecs);
  441.  
  442. {Don't forget to close the file and dispose of the dynamic object.  The
  443.  object's Done destructor will also dispose of the collections for you.}
  444.  
  445. BStatus := MyFile^.Close;
  446. dispose(MyFile, Done);
  447.  
  448. END.
  449.  
  450. .............................................................................
  451.  
  452. This should be enough to get you started using BTP with both non-extended
  453. and the get/step extended calls.  Again, refer to the unit's source code and
  454. to the example programs for a fuller understanding of the BTP structures and
  455. technique.
  456.  
  457.  
  458. ABOUT THE EXAMPLE PROGRAMS
  459. --------------------------
  460.  
  461. The README.1ST file for a list and description of the example programs.  They
  462. should be extremely helpful in getting you up to speed with the BTP product.
  463. Several of them are utility programs you can use "out of the box"!
  464.  
  465.  
  466. PARTING SHOTS
  467. -------------
  468.  
  469. ENJOY BTP PROGRAMMING!  BTP is not free...it is shareware.  Yours with no
  470. obligation for a 30 day trial period, you are expected to remit the $25
  471. registration fee if you use BTP.  Please refer to the README.1ST file for
  472. license terms and remittance instructions.
  473.  
  474. The most current version of BTP can usually be found in 3 places on Compu-
  475. serve if not available to you through your favorite BBS or shareware catalog:
  476.  
  477.    Borland's Programmer's Forum (go BPROGA), in download library 1
  478.    IBM Programmer's Forum (go IBMPRO), in download library 0 (new uploads) or
  479.                                           download library 5 (other languages)
  480.    Novell Netwire (go NOVA), download library 12 (independent developers),
  481.                              download library 14 (public domain),
  482.                              download library 17 (other new uploads)
  483.  
  484. Even if you choose not to register your copy of BTP, I would like to hear
  485. from you about your experiences with and reaction to using this unit.  In
  486. addition, of course, constructive criticism and suggestions are always
  487. welcome.
  488.  
  489.    John C. Leon
  490.    3807 Wood Gardens Court
  491.    Kingwood, TX  77339
  492.  
  493.    713-359-3641 (residence)
  494.    CIS #72426,2077
  495.